package com.oneguy.libupdate;

import android.content.Context;
import android.text.TextUtils;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;

/**
 * Created by ZuoShu on 17/1/12.
 */
public class Downloader implements Runnable {
    private static final String TAG = "Downloader";
    public static final int START_SUCCESS = 0;
    public static final int START_INVALID_ARGS = -1;
    public static final int START_TASK_RUNNING = -2;

    public static final int ERROR_NETWORK = -1001;
    public static final int ERROR_NO_SPACE = -1002;
    public static final int ERROR_FILE = -1003;

    private static final int TIMEOUT = 3000;
    private static final long NOTIFY_INTERNAL = 1000;
    private static final int BUFFER_LEN = 1024;
    private static final int TEN_MB = 10 * 1024 * 1024;

    private static Downloader mInstance;
    private volatile boolean isRunning;
    private Object mutex = new Object();
    private DownloadListener listener;

    private String remoteUrl;
    private String saveDir;
    private long totalLength;
    private long currentLength;
    private boolean installAfterDownload;
    private long lastNotifyTime;

    private Context context;

    public interface DownloadListener {
        void onSuccess(String path);

        void onFail(int code);

        void onProgress(long total, long current);
    }

    public static synchronized Downloader getInstance() {
        if (mInstance == null) {
            mInstance = new Downloader();
        }
        return mInstance;
    }

    private Downloader() {
    }

    public void setContext(Context context) {
        this.context = context;
    }

    public int start(String url, String saveDir, DownloadListener listener) {
        if (TextUtils.isEmpty(url) || TextUtils.isEmpty(saveDir)) {
            return START_INVALID_ARGS;
        }
        if (isRunning) {
            return START_TASK_RUNNING;
        }
        this.remoteUrl = url;
        this.saveDir = saveDir;
        this.listener = listener;
        Thread t = new Thread(this);
        t.start();
        return START_SUCCESS;
    }

    private void setRunning(boolean value) {
        synchronized (mutex) {
            isRunning = value;
        }
    }

    @Override
    public void run() {
        LogHelper.warn(TAG, "downloading:" + remoteUrl + " saving to:" + saveDir);
        setRunning(true);

        // clear dir
        File dir = new File(saveDir);
        FileUtils.deleteQuietly(dir);

        if (!dir.exists()) {
            dir.mkdirs();
        }

        HttpURLConnection connection = null;
        InputStream input = null;
        OutputStream outputStream = null;
        try {
            URL url = new URL(remoteUrl);
            connection = (HttpURLConnection) url
                    .openConnection();
            connection.setDoInput(true);
            connection.setConnectTimeout(TIMEOUT);
            connection.connect();
            input = connection.getInputStream();

            int remoteFileSize = connection.getContentLength();
            LogHelper.warn(TAG, "size:" + remoteFileSize + " getRomAvailableSize:" + StorageUtil.getRomAvailableSize()
                    + " getSDAvailableSize:" + StorageUtil.getSDAvailableSize()
                    + " length:" + connection.getHeaderField("Content-Length"));
            long available = StorageUtil.getSDAvailableSize();
            if (remoteFileSize > available - TEN_MB) {
                if (listener != null) {
                    listener.onFail(ERROR_NO_SPACE);
                }
                return;
            }
            String remoteFileName = url.getFile();
            File saveFile = new File(saveDir, remoteFileName);
            if (saveFile.getParentFile() != null && !saveFile.getParentFile().exists()) {
                saveFile.getParentFile().mkdirs();
            }
            saveFile.createNewFile();

            outputStream = new FileOutputStream(saveFile);

            byte[] buffer = new byte[BUFFER_LEN];
            currentLength = 0;
            totalLength = remoteFileSize;
            int length = 0;

            while (((length = input.read(buffer)) != -1) && isRunning) {
                outputStream.write(buffer, 0, length);
                currentLength += length;
                notifyListener(totalLength, currentLength);
            }

            if (listener != null && isRunning) {
                listener.onSuccess(saveFile.getAbsolutePath());
            }
        } catch (IOException e) {
            e.printStackTrace();
            setRunning(false);
            if (listener != null) {
                listener.onFail(ERROR_NETWORK);
            }
        } finally {
            IOUtils.closeQuietly(input);
            IOUtils.closeQuietly(outputStream);
            IOUtils.close(connection);
            setRunning(false);
        }
    }

    private boolean chmodFile(String filepath) {
        Process p = null;
        try {
            p = Runtime.getRuntime().exec("chmod 644 " + filepath);
            int status = p.waitFor();
            if (status == 0) {
                return true;
            } else {
                return false;
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return false;
    }

    private void notifyListener(long totalLength, long currentLength) {
        long now = System.currentTimeMillis();
        if (now - lastNotifyTime > NOTIFY_INTERNAL) {
            LogHelper.warn(TAG, "totalLength:" + totalLength + " currentLength:" + currentLength);
            if (listener != null) {
                listener.onProgress(totalLength, currentLength);
            }
            lastNotifyTime = now;
        }
    }

    public void stop() {
        setRunning(false);
    }

}

